/** An effect that can be attached to a mobile.
 *
 * Below are the valid members of the property bag.
 *@param name The name of the effect.
 *@param duration The duration of the effect in time units (< 0 means permanent, 0 means apply only).
 *@param mods The property modifications that this effect has. This is a property bag of all of the properties that will be changed and a numeric value that will be ADDED to that property. When applied to a mobile this is the same as an item. When applied to an item it effects the item's mods.
 *@param onAttach The function to be called when the effect is attached to a mobile or item. This occurs after mods have taken effect.
 *@param onRemove The function to be called when the effect is removed from a mobile or item. This occurs after mods have been reversed.
 */
function Effect(bag)
{
	this.name = "Effect";
	this.duration = 10;
	this.durationMax = 10;
	this.type = "buff";
	this.mods = {};
	this.flags = {};
	this.parent = null;
	
	util.mix(this, bag);
	this.durationMax = this.duration;
}
Effect.prototype = {};
Effect.prototype.isEffect = true;
/** Updates the effect with a given time period.
 * @param time The amount of time that has passed since the last update.
 */
Effect.prototype.update = function(time)
{
	if(this.type !== "perm")
	{
		this.duration -= time;
		if(this.duration < 0)
			this.parent.removeEffect(this);
	}
}
/** Try to merge another effect with this one.
 *@param effect The effect to merge with this one.
 *@return True if the merger was successful, false otherwise.
 */
Effect.prototype.merge = function(effect)
{
	if(effect.name != this.name)
		return false;
	switch(this.type)
	{
		case "attack":
			return false;
		// Perm effects take the higher of the two mods
		case "perm":
			for(var o in this.mods)
			{
				if(!this.mods.hasOwnProperty(o))
					continue;
				if(Math.abs(this.mods[o]) < Math.abs(effect.mods[o]))
					this.mods[o] = effect.mods[o];
			}
			break;
		// For buffs we add the durations together but average out the effects
		case "buff":
			var totalDuration = this.duration + effect.duration;
			for(var o in this.mods)
			{
				if(!this.mods.hasOwnProperty(o))
					continue;
				var e = (this.duration * this.mods[o]) + (effect.duration * effect.mods[o]);
				e /= totalDuration;
				e = Math.round(e);
				if(e == 0)
					delete this.mods[o];
				else
					this.mods[o] = e;
			}
			this.duration = this.durationMax = totalDuration;
			break;
		default:
			throw new Error("Unsupported effect type " + this.type);
	}
	
	// If we get here we've succesfully merged
	this.onRemove();
	this.onAttach(this.parent);
	return true;
}
/** Called when the effect attaches to a mobile.
 *@param parent The mobile attaching to.
 *@return True if the effect should be added to the mobile's effect list.
 */
Effect.prototype.onAttach = function(parent)
{
	var ret = true;
	this.parent = parent;
	switch(this.type)
	{
		case "attack":
			this.parent.applyMods(this.mods);
			this.parent.applyFlags(this.flags);
			ret = false;
			break;
		case "perm":
		case "buff":
			this.parent.applyMods(this.mods);
			this.parent.applyFlags(this.flags);
			break;
		default:
			throw new Error("Unhandled effect type " + this.type);
	}
	this.onAttachHook();
	return ret;
}
/** Called when the effect is removed from a mobile by whatever means.
 */
Effect.prototype.onRemove = function()
{
	switch(this.type)
	{
		case "attack":
			break;
		case "perm":
		case "buff":
			this.parent.removeMods(this.mods);
			this.parent.removeFlags(this.flags);
			break;
		default:
			throw new Error("Unhandled effect type " + this.type);
	}
	this.onRemoveHook();
}
/** Hook for the end of onAttach.
 */
Effect.prototype.onAttachHook = function() { }
/** Hook for the end of onRemove.
 */
Effect.prototype.onRemoveHook = function() { }
ron.registerCtor(Effect, "Effect");
